/*******************************************************************************
 Jimm - Mobile Messaging - J2ME ICQ clone
 Copyright (C) 2003-05  Jimm Project

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ********************************************************************************
 File: src/DrawControls/VirtualList.java
 Version: ###VERSION###  Date: ###DATE###
 Author(s): Artyomov Denis, Igor Palkin
 *******************************************************************************/

package DrawControls;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Font;
import java.io.*;

import DrawControls.ImageList;
import DrawControls.ListItem;
import DrawControls.PopupListCommands;
import jimm.ContactList;
//#sijapp cond.if target is "MIDP2" | target is "MOTOROLA"#
import jimm.Jimm;
//#sijapp cond.end#
//#sijapp cond.if target is "MOTOROLA"#
import DrawControls.LightControl;
//#sijapp cond.end#
import jimm.Options;
import jimm.comm.XStatus;
import jimm.comm.Util;
import jimm.comm.Icq;
import jimm.ContactList;


public abstract class PopupList extends Canvas
{
	/*! Use dotted mode of cursor. If item of list 
	 is selected, dotted rectangle drawn around  it*/
	public final static int SEL_DOTTED = 2;

	/*! Does't show cursor at selected item. */
	public final static int SEL_NONE = 3;

//	public static Font capFont;

	public static int heightFromTop = 2;

	public static ImageList imageList = null;

	public boolean menuLeft = false;

	/*! Constant for medium sized font of caption and item text */
	public final static int MEDIUM_FONT = Font.SIZE_MEDIUM;

	public boolean IsItATable = false;

	// Image object, holds the splash image
	private static Image bgimage;

	/*! Constant for large sized font of caption and item text */
	public final static int LARGE_FONT = Font.SIZE_LARGE;

	/*! Constant for small sized font of caption and item text */
	public final static int SMALL_FONT = Font.SIZE_SMALL;
	
	// Key event type
	public final static int KEY_PRESSED = 1;
	public final static int KEY_REPEATED = 2;
	public final static int KEY_RELEASED = 3;
	
	// Set of fonts for quick selecting
	public Font normalFont, boldFont, italicFont;
	
	// Commands to react to VL events
	private PopupListCommands vlCommands;

	// Caption of VL
//	private String caption;
	
	// Used by "Invalidate" method to prevent invalidate when locked 
	private boolean dontRepaint = false;

	// Index for current item of VL
	protected int currItem = 0;
	
	// Used for passing params of items whan painting 
	final static protected ListItem paintedItem;
	
	// Used to catch changes to repaint data
	private int lastCurrItem = 0, lastTopItem = 0;

	private static PopupList current;
	
	
	private int 
		topItem     = 0,            // Index of top visilbe item 
		fontSize    = MEDIUM_FONT,  // Current font size of VL
		bkgrndColor = 0xFFFFFF,     // bk color of VL
		cursorColor = 0x808080,     // Used when drawing focus rect.
		textColor   = 0x000000,     // Default text color.
		capBkCOlor  = 0xC0C0C0,
		capTxtColor = 0x00,     // Color of caprion text
		cursorMode  = SEL_DOTTED;   // Cursor mode
	
	static
	{
		paintedItem = new ListItem();
	}

	public void setBackImage(Image img)
	{
		bgimage = img;
	}
	
	//! Create new virtual list with default values  
	public PopupList
	(
	)
	{
		super();
		//#sijapp cond.if target is "MIDP2" | target is "SIEMENS2"# 
		this.fontSize = Font.SIZE_SMALL;
		//#sijapp cond.else#
		this.fontSize = Font.SIZE_MEDIUM;
		//#sijapp cond.end#
		createSetOfFonts(this.fontSize);
		this.cursorMode = SEL_DOTTED;
		//setFullScreenMode(true);
		current = this;
		current.setFullScreenMode(true);

	}

	// public VirtualList
	public PopupList
	(
		String capt,      //!< Caption text of new virtual list
		int capTextColor, //!< Caption text color
		int backColor,    //!< Control back color
		int fontSize,     /*!< Control font size. This font size if used both for caption and text in tree nodes */
		int cursorMode    /*!< Cursor mode. Can be VirtualList.SEL_DOTTED or VirtualList.SEL_INVERTED */
	)
	{
		super();
//		setCaption(capt);
		this.bkgrndColor = backColor;
		
		this.fontSize = fontSize; 
		createSetOfFonts(this.fontSize);
		this.cursorMode = cursorMode;
	}

	//! Request number of list elements to be shown in list
	/*! You must return number of list elements in successtor of
	    VirtualList. Class calls method "getSize" each time before it drawn */
	abstract protected int getSize();

	//! Request of data of one list item
	/*! You have to reload this method. With help of method "get" class finds out
	    data of each item. Method "get" is called each time when list item 
	    is drawn */
	abstract protected void get
	(
		int index,    //!< Number of requested list item 
		ListItem item //!< Data of list item. Fill this object with item data.
	);

	Font getQuickFont(int style)
	{
		switch (style)
		{
		case Font.STYLE_BOLD:   return boldFont;
		case Font.STYLE_PLAIN:  return normalFont;
		case Font.STYLE_ITALIC: return italicFont;
		}
		return Font.getFont(Font.FACE_SYSTEM, style, fontSize);
	}
	
	// returns height of draw area in pixels  
	protected int getDrawHeight()
	{
		return getHeightInternal();//-getCapHeight();
	}

	//! Sets new font size and invalidates items
	public void setFontSize(int value)
	{
		if (fontSize == value) return;
		fontSize = value;
		createSetOfFonts(fontSize);
		checkTopItem();
		invalidate();
	}
	
	
	public void setVLCommands(PopupListCommands vlCommands)
	{
		this.vlCommands = vlCommands;
	}
	
	public void setColors(int capTxt, int capbk, int bkgrnd, int cursor, int text)
	{
		capBkCOlor  = capbk;
		capTxtColor = capTxt;
		bkgrndColor = bkgrnd;
		cursorColor = cursor;
		textColor   = text;
		repaint();
	}
	
	private void createSetOfFonts(int size)
	{

		normalFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,  fontSize); 
		boldFont   = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,   fontSize);
		italicFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, fontSize);

	}
	
	public int getFontSize()
	{
		return fontSize;
	}

	public int getTextColor()
	{
		return textColor;
	}

	//! Returns number of visibled lines of text which fits in screen 
	public int getVisCount()
	{
		int size = getSize();
		int y = 0;
		int counter = 0, i;
		int height = getDrawHeight();
		int topItem = this.topItem;
		
		if (size == 0) return 0;
		
		if (topItem < 0) topItem = 0;
		if (topItem >= size) topItem = size-1;
		
		for (i = topItem; i < (size-1); i++)
		{
			y += getItemHeight(i);
			if (y > height) return counter;
			counter++;
		}
		
		y = height;
		counter = 0;
		for (i = size-1; i >= 0; i--)
		{
			y -= getItemHeight(i);
			if (y < 0) break;
			counter++;
		}
		
		return counter;
	}

	//TODO: brief text
	public void setCursorMode(int value)
	{
		if (cursorMode == value) return;
		cursorMode = value;
		invalidate();
	}
	
	public int getCursorMode()
	{
		return cursorMode;
	}
	
	//! Returns height of each item in list
	public int getItemHeight(int itemIndex)
	{
		//#sijapp cond.if target is "MOTOROLA"#
		int imgHeight, fontHeight = getFontHeight() - 1;
		//#sijapp cond.else#
		int imgHeight, fontHeight = getFontHeight();
		//#sijapp cond.end#
		if (imageList != null) imgHeight = imageList.getHeight() + 1;
		else imgHeight = 0;
		return (fontHeight > imgHeight) ? fontHeight : imgHeight;
	}

	// protected void invalidate()  
	protected void invalidate()
	{
		if (dontRepaint) return;
		repaint();
	}

	// Setting image list for items
	public void setImageList(ImageList list)
	{
		imageList = list;
		invalidate();
	}

	//! Return current image list, used for tree node icons
	/*! If no image list stored, null is returned */
	public ImageList getImageList()
	{
		return imageList;
	}

	// protected void checkCurrItem()
	protected void checkCurrItem()
	{
		if (Options.getBoolean(Options.OPTION_FLIP_CURSOR)) // && (getCursorMode() != SEL_NONE))
		{
			if (getSize() == 0)
			{
				currItem = 0;
				return;
			}

			if (currItem < 0)
			{
				currItem = getSize() - 1;
			}
			if ((currItem > getSize() - 1))
			{
				currItem = 0;
			}
			return;
		} else
		{
			if (currItem < 0) currItem = 0;
			if (currItem > getSize() - 1) currItem = getSize() - 1;
		}
	}
	
	// protected void checkTopItem() - internal
	// check for position of top element of list and change it, if nesessary
	protected void checkTopItem()
	{
		int size = getSize();
		int visCount = getVisCount();
		
		if (size == 0)
		{
			topItem = 0;
			return;
		}
		
		if (currItem >= (topItem + visCount - 1)) topItem = currItem - visCount + 1;
		if (currItem < topItem) topItem = currItem;
		
		if ((size - topItem) <= visCount) topItem = (size > visCount) ? (size - visCount) : 0;
		if (topItem < 0) topItem = 0;
	}

	// Check does item with index visible
	protected boolean visibleItem(int index)
	{
		return (index >= topItem) && (index <= (topItem + getVisCount()));
	}

	// private void storelastItemIndexes()
	protected void storelastItemIndexes()
	{
		lastCurrItem = currItem;
		lastTopItem = topItem;
	}

	// private void repaintIfLastIndexesChanged()
	protected void repaintIfLastIndexesChanged()
	{                           	
		if ((lastCurrItem != currItem) || (lastTopItem != topItem))
		{
			invalidate();
			if (vlCommands != null) vlCommands.onCursorMove(this);
		}
	}

	// private void repaintIfLastIndexesChanged()
	protected void repaintAnyway()
	{
		invalidate();
	}


	// protected void moveCursor(int step)
	protected void moveCursor(int step, boolean moveTop)
	{
		storelastItemIndexes();
		if (moveTop && (cursorMode == SEL_NONE)) topItem += step;
		currItem += step;
		checkCurrItem();
		checkTopItem();
		repaintIfLastIndexesChanged();
	}

	protected void itemSelected() {}
	
	// private keyReaction(int keyCode)
	private void keyReaction(int keyCode)
	{
		try
		{
			switch (getGameAction(keyCode))
			{
			case Canvas.LEFT:
				if (!IsItATable && Options.getBoolean(Options.OPTION_JUMP_CURSOR))
				{
					setTopItem(0);
				}
				break;
			case Canvas.RIGHT:
				if (!IsItATable && Options.getBoolean(Options.OPTION_JUMP_CURSOR))
				{
					setTopItem(getSize()-1-getVisCount()); 
					setCurrentItem(getSize()-1);
				}
				break;

			case Canvas.DOWN:
				moveCursor(1, false);
				return; //break;
			case Canvas.UP:
				moveCursor(-1, false);
				return; //break;

			case Canvas.FIRE:
				//#sijapp cond.if target is "SIEMENS2"#
				if(keyCode != -1 && keyCode != -4)
				//#sijapp cond.end#
				{
					itemSelected();
					if (vlCommands != null) vlCommands.onItemSelected(this);
				}
				break;
			}
		}
		catch (Exception e) // getGameAction throws exception on motorola when opening flipper
		{
			return;
		}

/*
		switch (keyCode)
		{
		case KEY_NUM1:
			storelastItemIndexes();
			currItem = topItem = 0;
			repaintIfLastIndexesChanged();
			break;
			
		case KEY_NUM7:
			storelastItemIndexes();
			int endIndex = getSize() - 1;
			currItem = endIndex;
			checkTopItem();
			repaintIfLastIndexesChanged();
			break;

		case KEY_NUM3:
			moveCursor(-getVisCount(), true);
			break;
			
		case KEY_NUM9:
			moveCursor(getVisCount(), true);
			break;
			
		// #sijapp cond.if target is "MOTOROLA"#
		case KEY_STAR: 
			LightControl.changeState();
			break;
		// #sijapp cond.end#
		}
*/
	}
	
	public void doKeyreaction(int keyCode, int type)
	{
		if (Jimm.lastKeyPressedSet)
		{

						Options.setLong(Options.OPTION_ONLINE_STATUS, Jimm.lastKeyPressedWasStatus);
						Jimm.gm.refreshMainMenu();
						Options.safe_save();

			try
			{
				Icq.setOnlineStatus(Jimm.lastKeyPressedWasStatus);
				Jimm.lastKeyPressedSet = false;
				ContactList.activate();

			}
			catch (Exception e) {}

		}
		Jimm.lastKeyPressedTime = Util.createCurrentDate(false);
		switch (type)
		{
		case KEY_PRESSED:
			//#sijapp cond.if target="MOTOROLA"#
			LightControl.flash(false);
			//#sijapp cond.end#
			keyReaction(keyCode);
			break;
		case KEY_REPEATED:
			keyReaction(keyCode);
			break;
		}
		
		if (vlCommands != null) vlCommands.onKeyPress(this, keyCode, type);
	}

	protected void keyPressed(int keyCode)
	{
		//System.out.println(keyCode);

		doKeyreaction(keyCode, KEY_PRESSED);
		//System.out.println("keypressed");
	}

	protected void keyRepeated(int keyCode)
	{
		doKeyreaction(keyCode, KEY_REPEATED);
		//System.out.println("repeated");
	}
	
	protected void keyReleased(int keyCode)
	{
		doKeyreaction(keyCode, KEY_RELEASED);
		//System.out.println("released");
	}
	
	//#sijapp cond.if target is "MIDP2"#
	private static long lastPointerTime = 0;
	private static int lastPointerYCrd = -1;
	private static int lastPointerXCrd = -1;
	private static int lastPointerTopItem = -1;
	
	protected void pointerDragged(int x, int y)
	{
		if (lastPointerTopItem == -1) return;
		int height = getHeightInternal();//-getCapHeight();
		int itemCount = getSize();
		int visCount = getVisCount();
		if (itemCount == visCount) return;
		storelastItemIndexes();
		topItem = lastPointerTopItem+(itemCount)*(y-lastPointerYCrd)/height;
		if (topItem < 0) topItem = 0;
		if (topItem > (itemCount-visCount)) topItem = itemCount-visCount; 
		repaintIfLastIndexesChanged();
	}
	
	protected boolean pointerPressedOnUtem(int index, int x, int y)
	{
		return false;
	}
	
	static int abs(int value)
	{
		return (value < 0) ? -value : value;
	}
	
	protected void pointerPressed(int x, int y)
	{
		int itemY1 = 0; //getCapHeight();
		
		// is pointing on data area
		lastPointerTopItem = -1;
		
		int size = getSize();
		for (int i = topItem; i < size; i++)
		{
			int height = getItemHeight(i);
			int itemY2 = itemY1+height;
			if ((itemY1 <= y) && (y < itemY2))
			{
				setCurrentItem(i);
				
				if (pointerPressedOnUtem(i, x, y) == false)
				{
					long time = System.currentTimeMillis();
					if (((time-lastPointerTime) < 500) && 
					     (abs(x-lastPointerXCrd) < 10) &&
					     (abs(y-lastPointerYCrd) < 10))
					{
						itemSelected();
						if (vlCommands != null) vlCommands.onItemSelected(this);
					}
					lastPointerTime = time;
				}
				break;
			}
			itemY1 = itemY2;
		}
		
		lastPointerXCrd = x;
		lastPointerYCrd = y;
	}
	//#sijapp cond.end#

	public void setTopItem(int index)
	{
		storelastItemIndexes();
		currItem = topItem = index;
		checkTopItem();
		repaintIfLastIndexesChanged();
	}

	// public void setCurrentItem(int index)
	public void setCurrentItem(int index)
	{
		storelastItemIndexes();
		currItem = index;
		checkTopItem();
		repaintIfLastIndexesChanged();
	}

	// public int getCurrIndex()
	public int getCurrIndex()
	{
		return currItem;
	}
	
	protected boolean isItemSelected(int index)
	{
		return ((currItem == index) && (cursorMode != SEL_NONE));
	}

	// private int drawItem(int index, Graphics g, int top_y, int th, ListItem item)
	private int drawItem(
		int index,
		Graphics g,
		int x0,
		int yCrd,
		int itemWidth,
		int itemHeight,
		int fontHeight)
	{
		drawItemData(g, (currItem == index), index, x0+2, yCrd, itemWidth-itemHeight/3, yCrd+itemHeight, fontHeight);
		return yCrd + itemHeight;
	}

	public int getFontHeight()
	{
		return getQuickFont(Font.STYLE_PLAIN).getHeight();
	}  

	// private int drawItems(Graphics g, int top_y)
	private int drawItems(Graphics g, int top_y, int fontHeight)
	{

		//current.setFullScreenMode(true);

		int grCursorY1 = -1, grCursorY2 = -1; 
		int height = getHeightInternal() - heightFromTop - VirtualList.lastFooterHeight;

	        top_y = height - getItemHeight(0)*getSize();


		int size = getSize();
		int i, y;

		int itemWidth = 0;

		ListItem itm = new ListItem();

		for (int r = 0; r < getSize(); r++)
		{
			get(r, itm);
			if (itm == null)
				continue;

			int n = VirtualList.getStringWidth(itm.text, getQuickFont(Font.STYLE_PLAIN)) + 5;

			if (n > itemWidth)
			{
				itemWidth = n;
			}
		}

		itemWidth += 2; //itmWidth;

		int cursColorBase = Options.getSchemeColor(Options.CLRSCHHEME_CURSOR); //Options.getInt(Options.OPTION_COLOR_CURSOR);
		
		// Fill background
		g.setColor(bkgrndColor);

		int x0 = (!Options.getBoolean(Options.OPTION_INVERSE_SOFTKEYS)) ? 0 : getWidthInternal()-itemWidth-2;

		Image bi = bgimage; //VirtualList.getBackImage();

		if (/*!isDoubleBuffered() &&*/ bi != null)
		{
			try
			{
				g.drawImage(bi, 0, 0, Graphics.TOP | Graphics.LEFT);
			}
			catch (Exception e)
			{
			}
		}

		g.fillRect(x0, top_y, itemWidth, height-top_y); //getHeightInternal());

		g.setColor(cursColorBase);
		
		g.drawLine(x0, top_y-1, x0+itemWidth-1, top_y-1);
		g.drawLine(x0, height+1, x0+itemWidth-1, height+1);
		g.drawLine(x0, top_y-1, x0, height+1);
		g.drawLine(x0+itemWidth-1, top_y-1, x0+itemWidth-1, height+1);



		// Draw cursor
		y = top_y;
		for (i = topItem; i < size; i++)
		{
			int itemHeight = getItemHeight(i);
			if (isItemSelected(i))
			{
				if (grCursorY1 == -1) grCursorY1 = y;
				grCursorY2 = y+itemHeight-1; 
			}
			y += itemHeight;
			if (y >= height) break;
		}
		
		if (grCursorY1 != -1)
		{
			if (Options.getBoolean(Options.OPTION_DOTTED_CURSOR))
			{
				g.setStrokeStyle(Graphics.DOTTED);
				g.setColor(cursColorBase); //Options.getSchemeColor(Options.CLRSCHHEME_CURSOR)); //cursorColor);
				//g.drawRect(0, grCursorY1+1, itemWidth-1, grCursorY2-1);
				g.drawLine(x0+1, grCursorY1, x0+itemWidth-2, grCursorY1);
				g.drawLine(x0, grCursorY1+1, x0, grCursorY2-1);
				g.drawLine(x0+itemWidth-1, grCursorY1+1, x0+itemWidth-1, grCursorY2-1);
				g.drawLine(x0+1, grCursorY2, x0+itemWidth-2, grCursorY2);
				
			}
			else
			{
			g.setStrokeStyle(Graphics.SOLID);

			g.setColor(cursColorBase); //0xFFFFFF/*cursorColor*/);

			g.fillRect(x0+2, grCursorY1+1, itemWidth-4, grCursorY2-grCursorY1);

			}

			
		}
		
		// Draw items
		paintedItem.clear();
		g.setColor(textColor);
		y = top_y;
		for (i = topItem; i < size; i++)
		{
			int itemHeight = getItemHeight(i);
			g.setStrokeStyle(Graphics.SOLID);
			y = drawItem(i, g, x0, y, itemWidth, itemHeight, fontHeight);
			if (y >= height) break;
		}

		return y;
	}

	void init()
	{
	}

	void destroy()
	{
	}
/*
	// change light of color 
	static private int transformColorLight(int color, int light)
	{
		int r = (color & 0xFF) + light;
		int g = ((color & 0xFF00) >> 8) + light;
		int b = ((color & 0xFF0000) >> 16) + light;
		if (r < 0) r = 0;
		if (r > 255) r = 255;
		if (g < 0) g = 0;
		if (g > 255) g = 255;
		if (b < 0) b = 0;
		if (b > 255) b = 255;
		return r | (g << 8) | (b << 16);
	}
*/
	public void paintAllOnGraphics(Graphics graphics)
	{
		int visCount = getVisCount();
		drawItems(graphics, 0, getFontHeight());
	}

	static Image bDIimage = null;

	// protected void paint(Graphics g)
	protected void paint(Graphics g)
	{
		if (dontRepaint) return;

		if (isDoubleBuffered())
		{
			paintAllOnGraphics(g);
		}
		else
		{         
			try
			{
				if (bDIimage == null) bDIimage = Image.createImage(getWidthInternal(), getHeightInternal());
				paintAllOnGraphics(bDIimage.getGraphics());
				g.drawImage(bDIimage, 0, 0, Graphics.TOP | Graphics.LEFT);
			}
			catch (Exception e)  
			{
				paintAllOnGraphics(g);
			}
		}
	}

	// protected void drawItemData
	protected void drawItemData(
		Graphics g,
		boolean isSelected,
		int index,
		int x1,
		int y1,
		int x2,
		int y2,
		int fontHeight)
	{
		int imgWidth = 0;

		get(index, paintedItem);
		
		g.setFont(getQuickFont(paintedItem.fontStyle));
		g.setColor(paintedItem.color);

		if (paintedItem.text != null) 
			g.drawString
			(
				paintedItem.text, 
				x1 + imgWidth+2, 
				(y1 + y2 - fontHeight) / 2, 
				Graphics.TOP | Graphics.LEFT
			);
	}

	public void lock()
	{
		dontRepaint = true;
	}

	protected void afterUnlock()
	{
	}

	public void unlock()
	{
		dontRepaint = false;
		afterUnlock();
		invalidate();
	}

	protected boolean getLocked()
	{
		return dontRepaint;
	}
	
	private int forcedWidth = -1;
	private int forcedHeight = -1;
	
	public void setForcedSize(int width, int height)
	{
		forcedWidth = width;
		forcedHeight = height;
	}
	
	protected int getHeightInternal()
	{
		return (forcedHeight == -1) ? getHeight() : forcedHeight;
	}
	
	protected int getWidthInternal()
	{
		return (forcedWidth == -1) ? getWidth() : forcedWidth;
	}
}